/*
    Copyright (C) 2008-2009 Dmitry Gusev <dmitry.gusev@gmail.com>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

 */

package dmitrygusev.web4konverta.mobile;

import java.io.IOException;
import java.io.OutputStream;
import java.util.Date;
import java.util.Hashtable;
import java.util.Vector;
import javax.microedition.io.Connector;
import javax.microedition.io.HttpConnection;
import net.dclausen.microfloat.MicroDouble;

/**
 *
 * @author dmitrygusev
 */
public class EnvelopeController {
    private static final ApplicationState as = ApplicationState.getInstance();;
    
    private String plainHtml;
    private String envelopeURL;
    private Date envelopeMonday;
    private Date envelopeSunday;
    
    private Vector persons;

    private MicroDouble weeklyBudget;
    private MicroDouble dailyBudget;

    private Vector spentForADay;
    
    public EnvelopeController(String plainHtml, String envelopeURL, Date envelopeMonday, Date envelopeSunday) {
        this.plainHtml = plainHtml;
        this.envelopeURL = envelopeURL;
        this.envelopeMonday = envelopeMonday;
        this.envelopeSunday = envelopeSunday;
        
        spentForADay = new Vector(7);
        for (int i = WeeklyEnvelope.MONDAY; i <= WeeklyEnvelope.SUNDAY; i++) {
            spentForADay.addElement(new Hashtable(3));
        }
    }

    public MicroDouble getDailyBudget() {
        return dailyBudget;
    }

    public MicroDouble getWeeklyBudget() {
        return weeklyBudget;
    }
    
    public Date getEnvelopeMonday() {
        return envelopeMonday;
    }
    
    public Date getEnvelopeSunday() {
        return envelopeSunday;
    }

    public String getEnvelopeURL() {
        return envelopeURL;
    }

    public String getPlainHtml() {
        return plainHtml;
    }
    
    public Vector getPersons()
    {
        if (persons == null)
        {
            persons = parsePersons();
        }
        
        return persons;
    }

    boolean includes(Date date) {
        return date.getTime() >= getEnvelopeMonday().getTime()
                && date.getTime() <= getEnvelopeSunday().getTime();
    }

    void saveExpenses(int dow, String personId, String expenses)
            throws IOException, Exception
    {
        HttpConnection conn = null;
        
        try
        {
            conn = (HttpConnection) Connector.open(
                    getEnvelopeURL() + "saveExpression");

            String msg = "expr=" + Utils.encode(expenses) 
                       + "&key=" + dow + "-" + personId;

            conn.setRequestMethod(HttpConnection.POST);
            
            conn.setRequestProperty("Cookie", as.getCookie());
            conn.setRequestProperty("Content-Length", "" + msg.length());
            conn.setRequestProperty("Content-Type", 
                    "application/x-www-form-urlencoded");

            OutputStream os = conn.openOutputStream();

            os.write(msg.getBytes());

            int rc = conn.getResponseCode();

            if (rc != 200) {
                throw new Exception(
                        "Ошибка 4k-m-008: Не могу записать затраты " 
                        + "(HTTP 200 expected but was " + rc + ")");
            }
        } finally {
            if (conn != null)
            {
                try { conn.close(); } catch (IOException e) {}
                conn = null;
            }
        }
    }

    private WeeklyEnvelope parsePersonExpenses(Person person, String envelopeHtml)
    {
        WeeklyEnvelope result = new WeeklyEnvelope();
        
        /*
         * name="expenses['0-393216']" value="20 на проезд"
         */
        
        for (int day = WeeklyEnvelope.MONDAY; day <= WeeklyEnvelope.SUNDAY; day++)
        {
            String expensesPreambula = 
                    "name=\"expenses['" 
                    + day + "-" + person.getId() 
                    + "']\" value=\"";
            
            int idx1 = envelopeHtml.indexOf(expensesPreambula);
            int idx2 = envelopeHtml.indexOf(
                    "\"", idx1 + expensesPreambula.length());
            
            String expenses = envelopeHtml.substring(
                    idx1 + expensesPreambula.length(), idx2);
            
            result.setExpensesForDay(day, expenses);
            
            String calculatedExpensesPreambula = 
                    "onchange=\"editEnvelopeValue('" + day + "-" + person.getId() + "')";
            idx1 = envelopeHtml.indexOf(calculatedExpensesPreambula) 
                    + calculatedExpensesPreambula.length();
            String valuePreambula = "value=\"";
            idx1 = envelopeHtml.indexOf(valuePreambula, idx1) 
                    + valuePreambula.length();
            idx2 = envelopeHtml.indexOf("\"", idx1);

            String value = envelopeHtml.substring(idx1, idx2);

            setSpentForADay(day, person.getId(), 
                Utils.isNullOrEmpty(value) 
                    ? new MicroDouble(MicroDouble.ZERO) 
                    : new MicroDouble(value));
        }
        
        return result;
    }

    private Vector parsePersons() {

        Vector result = new Vector();

        /*
        var personIds = new Array();
        personIds[personIds.length] = '393216';
        personIds[personIds.length] = '393217';
         */

        String personIdPreambula = "personIds[personIds.length] = '";

        int idx1 = plainHtml.indexOf(personIdPreambula);

        int idx2 = 0;

        do {
            int fromIndex = idx1 + personIdPreambula.length();

            idx2 = plainHtml.indexOf("'", fromIndex);

            String personId = plainHtml.substring(fromIndex, idx2);

            Person p = new Person("", personId);

            result.addElement(p);

            idx1 = plainHtml.indexOf(personIdPreambula, idx2);
        } while (idx1 != -1);

        String personNamePreambula = "<tr class=\"row0\">";

        idx1 = plainHtml.indexOf(personNamePreambula);

        /*
        <tr class="row0">
        <th>&nbsp;</th>
        <th>Дима</th>
        <th>Настя</th>
         */

        idx2 = plainHtml.indexOf("<th>", idx1);

        for (int i = 0; i < result.size(); i++) {
            idx1 = plainHtml.indexOf("<th>", idx2 + "<th>".length());
            idx2 = plainHtml.indexOf("</th>", idx1);

            String personName = plainHtml.substring(idx1 + "<th>".length(), idx2);

            ((Person) result.elementAt(i)).setName(personName);
        }

        for (int i = 0; i < result.size(); i++) {
            final Person person = (Person) result.elementAt(i);

            WeeklyEnvelope envelope = parsePersonExpenses(person, plainHtml);

            person.setEnvelope(envelope);
        }

        String weeklyBudgetPreambula = "var weeklyBudget = ";
        idx1 = plainHtml.indexOf(weeklyBudgetPreambula) + weeklyBudgetPreambula.length();
        idx2 = plainHtml.indexOf(";", idx1);

        String weeklyBudgetString = plainHtml.substring(idx1, idx2);

        weeklyBudget = new MicroDouble(weeklyBudgetString);

        String dailyBudgetPreambula = "var dailyBudget = ";
        idx1 = plainHtml.indexOf(dailyBudgetPreambula) + dailyBudgetPreambula.length();
        idx2 = plainHtml.indexOf(";", idx1);

        String dailyBudgetString = plainHtml.substring(idx1, idx2);

        dailyBudget = new MicroDouble(dailyBudgetString);

        return result;
    }

    public MicroDouble getLeftForADayTotal(int dow) {
        Utils.assertDowInRange(dow);

        MicroDouble leftBefore = weeklyBudget;
        
        for (int i = WeeklyEnvelope.MONDAY; i < dow; i++) {
            leftBefore = new MicroDouble(MicroDouble.sub(
                    leftBefore.doubleValue(), 
                    getSpentForADayTotal(i).doubleValue()));
        }
        
        int days = WeeklyEnvelope.SUNDAY - (dow - 1);
        
        MicroDouble budgetForADay = new MicroDouble(MicroDouble.div(
                leftBefore.doubleValue(), 
                new MicroDouble(String.valueOf(days)).doubleValue()));
        
        return new MicroDouble(MicroDouble.sub(
                budgetForADay.doubleValue(), 
                getSpentForADayTotal(dow).doubleValue()));
    }

    public MicroDouble getSpentForADayTotal(int dow) {
        MicroDouble result = new MicroDouble(MicroDouble.ZERO);
        
        for (int i = 0; i < persons.size(); i++) {
            Person person = (Person) persons.elementAt(i);
            MicroDouble value = getSpentForADay(dow, person.getId());
            
            result = new MicroDouble(MicroDouble.add(
                    result.doubleValue(), 
                    value.doubleValue()));
        }
        
        return result;
    }
    
    private MicroDouble getSpentForADay(int dow, String personId) {
        Utils.assertDowInRange(dow);
        
        Hashtable map = (Hashtable) spentForADay.elementAt(dow);
        return (MicroDouble) map.get(personId);
    }

    private void setSpentForADay(int dow, String id, MicroDouble value) {
        Utils.assertDowInRange(dow);

        Hashtable map = (Hashtable) spentForADay.elementAt(dow);
        map.put(id, value);
    }

    public MicroDouble getSpentForTheWeek() {
        MicroDouble totalSpent = new MicroDouble(MicroDouble.ZERO);

        for (int dow = WeeklyEnvelope.MONDAY; dow <= WeeklyEnvelope.SUNDAY; dow++) {
            totalSpent = new MicroDouble(
                    MicroDouble.add(
                        totalSpent.doubleValue(),
                        getSpentForADayTotal(dow).doubleValue()));
        }

        return totalSpent;
    }

    public MicroDouble getLeftForTheWeek() {
        return new MicroDouble(MicroDouble.sub(
                weeklyBudget.doubleValue(),
                getSpentForTheWeek().doubleValue()));
    }

}
